package com.example.diancai.websocket.logic;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import com.example.diancai.dto.CurrentPlayer;
import com.example.diancai.dto.Vec3DTO;
import com.example.diancai.entity.GameRecord;
import com.example.diancai.entity.WxUser;
import com.example.diancai.service.GameRecordService;
import com.example.diancai.service.GameUserService;
import com.example.diancai.service.WxUserService;
import com.example.diancai.util.RedisUtil;
import com.example.diancai.websocket.control.WebSocketControl;
import com.example.diancai.websocket.dto.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.websocket.Session;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;

@Service
@Slf4j
public class GameLogic {

    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    @Lazy
    private WebSocketControl webSocketControl;

    @Autowired
    private GameUserService gameUserService;

    @Autowired
    private GameRecordService gameRecordService;
    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private AiLogic aiLogic;

    @Autowired
    private WxUserService wxUserService;
    /**
     * key房间编号
     * value ClientInfoDTO
     */
    public static final ConcurrentHashMap<String, RoofInfoDTO> roofSocketMap = new ConcurrentHashMap<String, RoofInfoDTO>();

    /**
     * 匹配队列
     */
    public static final ConcurrentLinkedQueue<ClientInfoDTO> match2048Linked = new ConcurrentLinkedQueue<>();

    public static final ConcurrentLinkedQueue<ClientInfoDTO> matchxiaoxiaoleLinked = new ConcurrentLinkedQueue<>();


    public void match(ClientInfoDTO clientInfoDTO, String gameType, boolean isGamePanel) {
        // 已经开始匹配了
        if (clientInfoDTO.isMatch()) {
            // 稍等
            return;
        }
        // 查找所有玩家是否有正在匹配的玩家
        ClientInfoDTO poll = null;
        if (gameType.equals("2048")) {
            if (CollUtil.isNotEmpty(match2048Linked)) {
                poll = match2048Linked.poll();
            }

        } else if (gameType.equals("xiaoxiaole")) {
            if (CollUtil.isNotEmpty(matchxiaoxiaoleLinked)) {
                poll = matchxiaoxiaoleLinked.poll();
            }
        }

        if (poll != null) {
            // 匹配成功

            this.clearInfo(poll);
            this.clearInfo(clientInfoDTO);
            // 生成房间编号
            String roofNo = IdUtil.simpleUUID();
            poll.setRoofNo(roofNo);
            poll.setRoofNumber(1);
            poll.setMatch(false);
            poll.setScore(0);


            clientInfoDTO.setRoofNo(roofNo);
            clientInfoDTO.setRoofNumber(2);
            clientInfoDTO.setScore(0);

            if (gameType.equals("2048")) {
                poll.setLv(GameUserService.lv2048);
                poll.setBefore(GameUserService.init2048());

                clientInfoDTO.setLv(GameUserService.lv2048);
                clientInfoDTO.setBefore(GameUserService.init2048());


            } else if (gameType.equals("xiaoxiaole")) {
                poll.setLv(GameUserService.lvXiaoXiaoLe);
                poll.setBefore(GameUserService.initXiaoxiaole());

                clientInfoDTO.setLv(GameUserService.lvXiaoXiaoLe);
                clientInfoDTO.setBefore(GameUserService.initXiaoxiaole());
            }


            RoofInfoDTO roofInfoDTO = new RoofInfoDTO();
            roofInfoDTO.setRoofNo(roofNo);
            roofInfoDTO.setStatus(1);
            roofInfoDTO.setPlayers(CollUtil.newArrayList(poll, clientInfoDTO));
            roofInfoDTO.setDateTime(LocalDateTime.now());
            roofInfoDTO.setStartDateTime(roofInfoDTO.getDateTime().plusSeconds(5));
            roofInfoDTO.setEndDateTime(roofInfoDTO.getStartDateTime().plusMinutes(2));
            roofInfoDTO.setGameType(gameType);

            //  this.test(roofInfoDTO);

            roofSocketMap.put(roofNo, roofInfoDTO);

            // 发送匹配成功
            SocketMessage socketMessage = new SocketMessage();
            socketMessage.setEvent(parentPatch(roofInfoDTO.getGameType()) + "/matchSuccess");
            socketMessage.setData(roofInfoDTO);
            sendMessage(poll, socketMessage);
            sendMessage(clientInfoDTO, socketMessage);
            socketMessage.setEvent("home/matchSuccess");
            sendMessage(poll, socketMessage);
            sendMessage(clientInfoDTO, socketMessage);
            return;
        }


        if (gameType.equals("2048")) {
            match2048Linked.offer(clientInfoDTO);
        } else if (gameType.equals("xiaoxiaole")) {
            matchxiaoxiaoleLinked.offer(clientInfoDTO);
        }
        // 没有匹配到 等待匹配
        clientInfoDTO.setMatch(true);
        clientInfoDTO.setMatchTime(LocalDateTime.now());

        SocketMessage socketMessage = new SocketMessage();
        socketMessage.setEvent("home/matchPending");
        sendMessage(clientInfoDTO, socketMessage);
    }

    private void test(RoofInfoDTO roofInfoDTO) {
        for (ClientInfoDTO player : roofInfoDTO.getPlayers()) {
            Prop prop = new Prop();
            prop.setId("1");
            prop.setType(100);

            Prop prop2 = new Prop();
            prop2.setId("2");
            prop2.setType(101);

            Prop prop3 = new Prop();
            prop3.setId("3");
            prop3.setType(150);

            Prop prop4 = new Prop();
            prop4.setId("4");
            prop4.setType(151);
            player.setProps(CollUtil.newArrayList(prop, prop2, prop3, prop4));
        }
    }


    public void sendMessage(ClientInfoDTO clientInfoDTO, SocketMessage socketMessage) {
        try {

            log.info("发送消息:" + objectMapper.writeValueAsString(socketMessage));
            if (!clientInfoDTO.isAI() && clientInfoDTO.getSession().isOpen()) {
                clientInfoDTO.getSession().getBasicRemote().sendText(objectMapper.writeValueAsString(socketMessage));
            } else if (clientInfoDTO.isAI()) {
                this.aiAccept(clientInfoDTO, socketMessage);
            } else {
                log.error("连接已关闭");
            }
        } catch (IOException e) {
            log.info("发送消息失败:{},", socketMessage, e);
        }
    }

    private void aiAccept(ClientInfoDTO clientInfoDTO, SocketMessage socketMessage) {
        aiLogic.accept(clientInfoDTO, socketMessage);
    }

    public void sendMessage(List<ClientInfoDTO> clientInfoDTOS, SocketMessage socketMessage) {
        for (ClientInfoDTO clientInfoDTO : clientInfoDTOS) {
            this.sendMessage(clientInfoDTO, socketMessage);
        }
    }

    /**
     * 获取房间信息
     *
     * @param entity
     */
    public RoofInfoDTO getRoomInfoByClient(ClientInfoDTO entity) {
        return roofSocketMap.get(entity.getRoofNo());
    }

    /**
     * 获取房间信息
     *
     * @param entity
     */
    public void getRoomInfo(ClientInfoDTO entity) {
        RoofInfoDTO roofInfoDTO = roofSocketMap.get(entity.getRoofNo());
        SocketMessage<RoofInfoDTO> socketMessage = new SocketMessage<RoofInfoDTO>();
        socketMessage.setEvent("setRoofInfo");
        socketMessage.setData(roofInfoDTO);
        sendMessage(entity, socketMessage);
    }

    public void offline(ClientInfoDTO clientInfoDTO) {
        clientInfoDTO.setOnline(false);
        // 如果在房间里,给另外一个人发送离线消息
        if (StrUtil.isNotEmpty(clientInfoDTO.getRoofNo())) {
            RoofInfoDTO roofInfoDTO = roofSocketMap.get(clientInfoDTO.getRoofNo());
            if (roofInfoDTO != null) {

                for (ClientInfoDTO player : roofInfoDTO.getPlayers()) {
                    if (player != clientInfoDTO && player.isOnline()) {
                        SocketMessage socketMessage = new SocketMessage();
                        socketMessage.setEvent("opponentOffice");
                        sendMessage(player, socketMessage);
                    }

                }

            }
        }
        // 如果在匹配中,清除
        match2048Linked.remove(clientInfoDTO);
        matchxiaoxiaoleLinked.remove(clientInfoDTO);
    }

    public void updateGameInfo(ClientInfoDTO entity, String message) {
        SocketMessage<ClientInfoDTO> newClient = JSON.parseObject(message, new TypeReference<SocketMessage<ClientInfoDTO>>() {
        });
        entity.setBefore(newClient.getData().getBefore());
        entity.setCurrent(newClient.getData().getCurrent());
        entity.setScore(newClient.getData().getScore());
        entity.setDirective(newClient.getData().getDirective());
        entity.setOpponentCurrentScore(newClient.getData().getOpponentCurrentScore());
        // 通知另外一个人,对方移动了

        RoofInfoDTO roofInfoDTO = roofSocketMap.get(entity.getRoofNo());
        SocketMessage socketMessage = new SocketMessage();
        socketMessage.setEvent(parentPatch(roofInfoDTO.getGameType()) + "/rivalMove");

        socketMessage.setData(entity);
        for (ClientInfoDTO player : roofInfoDTO.getPlayers()) {
            if (!player.getCurrentPlayer().getId().equals(entity.getCurrentPlayer().getId())) {
                sendMessage(player, socketMessage);
            }
        }
    }

    public void exit(ClientInfoDTO clientInfoDTO) {
        // 已经存在匹配里则移除
        match2048Linked.remove(clientInfoDTO);
        matchxiaoxiaoleLinked.remove(clientInfoDTO);
        // 结算,通知对手已退出
        String roofNo = clientInfoDTO.getRoofNo();
        if (!roofSocketMap.containsKey(roofNo)) {
            return;
        }
        RoofInfoDTO roofInfoDTO = roofSocketMap.get(roofNo);

        if (roofInfoDTO.getStatus() == 0) {
            // 判断是不是房主
            SocketMessage socketMessage = new SocketMessage();
            socketMessage.setData("manyGame2048");
            exitRoom(clientInfoDTO, JSON.toJSONString(socketMessage));
            return;
        }
        if (roofInfoDTO.getStatus() == 2) {
            clearInfo(clientInfoDTO);
            if (isEmptyPlayer(roofInfoDTO.getPlayers())) {
                removeRoof(roofNo);
            }
        }

        List<ClientInfoDTO> players = roofInfoDTO.getPlayers();
        ClientInfoDTO clientInfoDTO1 = players.get(0);
        ClientInfoDTO clientInfoDTO2 = players.get(1);

        GameRecord gameRecord = new GameRecord();
        gameRecord.setRoofNo(roofNo);
        gameRecord.setWxUserOneId(clientInfoDTO1.getCurrentPlayer().getId());
        gameRecord.setWxUserTwoId(clientInfoDTO2.getCurrentPlayer().getId());
        gameRecord.setCreateTime(roofInfoDTO.getDateTime());
        gameRecord.setOneScore(clientInfoDTO1.getScore());
        gameRecord.setTwoScore(clientInfoDTO2.getScore());
        gameRecord.setLv(clientInfoDTO1.getLv());
        gameRecord.setOneSquare(JSON.toJSONString(clientInfoDTO1.getCurrent()));
        gameRecord.setTwoSquare(JSON.toJSONString(clientInfoDTO2.getCurrent()));
        if (clientInfoDTO1 == clientInfoDTO) {
            gameRecord.setWin(2);
            gameRecord.setOneOffice(0);
            gameRecord.setTwoOffice(1);
        } else {
            gameRecord.setWin(1);
            gameRecord.setOneOffice(1);
            gameRecord.setTwoOffice(0);
        }
        gameRecordService.saveRecord(gameRecord, clientInfoDTO1, clientInfoDTO2);

        // 删除房间信息

        //有一人退出了房间,就清除房间
        removeRoof(roofNo);

        // 清除信息
        clearInfo(clientInfoDTO1);
        clearInfo(clientInfoDTO2);

        // 获取对手
        List<ClientInfoDTO> opponent = this.getOpponent(roofInfoDTO.getPlayers(), clientInfoDTO);
        for (ClientInfoDTO infoDTO : opponent) {
            // 给房间其他人发送胜利消息
            SocketMessage socketMessage = new SocketMessage();
            socketMessage.setEvent(parentPatch(roofInfoDTO.getGameType()) + "/win");
            socketMessage.setData(1);
            sendMessage(infoDTO, socketMessage);
        }


    }

    /**
     * 获取对手
     *
     * @param clientInfoDTOS
     * @param clientInfoDTO
     * @return
     */
    public List<ClientInfoDTO> getOpponent(List<ClientInfoDTO> clientInfoDTOS, ClientInfoDTO clientInfoDTO) {
        List<ClientInfoDTO> result = new ArrayList<>();
        for (ClientInfoDTO infoDTO : clientInfoDTOS) {
            if (clientInfoDTO != infoDTO) {
                result.add(infoDTO);
            }
        }
        return result;
    }

    public void reconnect(ClientInfoDTO clientInfoDTO) {
        // 重连,通知其他人,重连了
        String roofNo = clientInfoDTO.getRoofNo();
        if (StrUtil.isEmpty(roofNo)) {
            return;
        }
        log.info("房间号:{}", roofNo);
        RoofInfoDTO roofInfoDTO = roofSocketMap.get(roofNo);
        if (roofInfoDTO == null) {
            // 房间已经解散
            return;
        }

        this.reconnectSuccess(clientInfoDTO);

        List<ClientInfoDTO> opponent = this.getOpponent(roofInfoDTO.getPlayers(), clientInfoDTO);
        for (ClientInfoDTO infoDTO : opponent) {
            // 给房间其他人发送重连消息
            SocketMessage socketMessage = new SocketMessage();
            socketMessage.setEvent("opponentReconnect");
            socketMessage.setData(clientInfoDTO.getCurrentPlayer().getId());
            sendMessage(infoDTO, socketMessage);
        }
    }

    public void match2048(ClientInfoDTO entity) {
        this.match(entity, "2048", true);
    }

    public void reconnectSuccess(ClientInfoDTO entity) {
        // 首页重连，进入房间
        if (StrUtil.isEmpty(entity.getRoofNo())) {
            return;
        }
        String roofNo = entity.getRoofNo();
        log.info("房间号:{}", roofNo);
        RoofInfoDTO roofInfoDTO = roofSocketMap.get(roofNo);
        if (roofInfoDTO == null
                || roofInfoDTO.getStatus() == 2
        ) {
            return;
        }

        SocketMessage socketMessage = new SocketMessage();
        socketMessage.setEvent("home/matchSuccess");
        socketMessage.setData(roofInfoDTO);
        sendMessage(entity, socketMessage);

    }

    public void failure(ClientInfoDTO clientInfoDTO, String message) {
        // 失败，给另外一方发送失败信息
        String roofNo = clientInfoDTO.getRoofNo();
        if (!roofSocketMap.containsKey(roofNo)) {
            log.error("未找到房间:{}", roofNo);
            return;
        }

        RoofInfoDTO roofInfoDTO = roofSocketMap.get(roofNo);

        List<ClientInfoDTO> players = roofInfoDTO.getPlayers();
        ClientInfoDTO clientInfoDTO1 = players.get(0);
        ClientInfoDTO clientInfoDTO2 = players.get(1);

        GameRecord gameRecord = new GameRecord();
        gameRecord.setRoofNo(roofNo);
        gameRecord.setWxUserOneId(clientInfoDTO1.getCurrentPlayer().getId());
        gameRecord.setWxUserTwoId(clientInfoDTO2.getCurrentPlayer().getId());
        gameRecord.setCreateTime(roofInfoDTO.getDateTime());
        gameRecord.setOneScore(clientInfoDTO1.getScore());
        gameRecord.setTwoScore(clientInfoDTO2.getScore());
        gameRecord.setLv(clientInfoDTO1.getLv());
        gameRecord.setOneSquare(JSON.toJSONString(clientInfoDTO1.getCurrent()));
        gameRecord.setTwoSquare(JSON.toJSONString(clientInfoDTO2.getCurrent()));
        if (clientInfoDTO1 == clientInfoDTO) {
            gameRecord.setWin(2);
            gameRecord.setOneOffice(0);
            gameRecord.setTwoOffice(1);
        } else {
            gameRecord.setWin(1);
            gameRecord.setOneOffice(1);
            gameRecord.setTwoOffice(0);
        }
        gameRecordService.saveRecord(gameRecord, clientInfoDTO1, clientInfoDTO2);
        roofInfoDTO.setStatus(2);
        clientInfoDTO1.setStatus(0);
        clientInfoDTO2.setStatus(0);
        // 删除房间信息
        //   roofSocketMap.remove(roofNo);
        // 清除信息

        //this.clearInfo(clientInfoDTO1);
        //this.clearInfo(clientInfoDTO2);


        // 获取对手
        List<ClientInfoDTO> opponent = this.getOpponent(roofInfoDTO.getPlayers(), clientInfoDTO);
        for (ClientInfoDTO infoDTO : opponent) {
            // 给房间其他人发送胜利消息
            SocketMessage socketMessage = new SocketMessage();
            socketMessage.setEvent(parentPatch(roofInfoDTO.getGameType()) + "/win");
            socketMessage.setData(1);
            sendMessage(infoDTO, socketMessage);
        }
    }

    private void clearInfo(ClientInfoDTO clientInfoDTO) {
        // 如果之前存在房间则移除
        if (StrUtil.isNotEmpty(clientInfoDTO.getRoofNo())
                && roofSocketMap.containsKey(clientInfoDTO.getRoofNo())) {
            RoofInfoDTO roofInfoDTO = roofSocketMap.get(clientInfoDTO.getRoofNo());
            roofInfoDTO.getPlayers().remove(clientInfoDTO);
            if (isEmptyPlayer(roofInfoDTO.getPlayers())) {
                removeRoof(clientInfoDTO.getRoofNo());
            }
        }
        clientInfoDTO.setBefore(null);
        clientInfoDTO.setCurrent(null);
        clientInfoDTO.setRoofNo(null);
        clientInfoDTO.setRoofNumber(0);
        clientInfoDTO.setProps(new ArrayList<>());
        clientInfoDTO.setScore(0);
        clientInfoDTO.setStatus(0);
    }


    public void useProp(ClientInfoDTO clientInfoDTO, String message) {

        SocketMessage<UsePropDTO> userPropMessage = JSON.parseObject(message, new TypeReference<SocketMessage<UsePropDTO>>() {
        });

        String roofNo = clientInfoDTO.getRoofNo();
        if (!roofSocketMap.containsKey(roofNo)) {
            log.error("未找到房间:{}", roofNo);
            return;
        }
        RoofInfoDTO roofInfoDTO = roofSocketMap.get(roofNo);

        // 幸运方块
        if (userPropMessage.getData().getProp().getType() == 150) {
            this.luckyCubesSuccess(clientInfoDTO, userPropMessage.getData());
        }

        // 获取对手
        List<ClientInfoDTO> opponent = getOpponent(roofInfoDTO.getPlayers(), clientInfoDTO);
        for (ClientInfoDTO infoDTO : opponent) {
            SocketMessage socketMessage = new SocketMessage(parentPatch(roofInfoDTO.getGameType()) + "/opponentUseProp",
                    userPropMessage.getData());
            sendMessage(infoDTO, socketMessage);
        }

    }

    /**
     * 幸运方块
     *
     * @param clientInfoDTO
     * @param usePropDTO
     */
    private void luckyCubesSuccess(ClientInfoDTO clientInfoDTO, UsePropDTO usePropDTO) {
        List<Vec3DTO> selectMePosition = usePropDTO.getSelectMePosition();
        if (CollUtil.isNotEmpty(selectMePosition)) {
            Vec3DTO vec3DTO = selectMePosition.get(0);
            vec3DTO.setZ(vec3DTO.getZ() * 2);
        }
    }

    private String parentPatch(String gameType) {
        if (gameType.equals("2048")) {
            return "manyGame2048";
        } else if (gameType.equals("xiaoxiaole")) {
            return "xiaoxiaoleManey";
        }
        return null;
    }


    public void addProp(ClientInfoDTO entity, String message) {

        if (entity.getProps() == null) {
            entity.setProps(new ArrayList<>());
        }
        List<Prop> props = entity.getProps();

        SocketMessage<String> socketMessage = JSON.parseObject(message, new TypeReference<SocketMessage<String>>() {
        });
        String id = socketMessage.getData().split("-")[0];
        String type = socketMessage.getData().split("-")[1];
        Prop prop = new Prop(id, Integer.parseInt(type));
        props.add(prop);
    }

    public void findIsGameOver() {
        LocalDateTime now = LocalDateTime.now();
        for (String key : roofSocketMap.keySet()) {
            //   log.info("房间个数:{}", roofSocketMap.size());

            RoofInfoDTO roofInfoDTO = roofSocketMap.get(key);
         /*   log.info("房间状态:{}", roofInfoDTO.getStatus());
            log.info("房间玩家:{}", roofInfoDTO.getPlayers().stream()
                    .map(item -> item.getCurrentPlayer().getWxUsername()).toList());*/


            if (roofInfoDTO.getStatus() == 1) {
                // 游戏没有结束
                if (roofInfoDTO.getEndDateTime().isBefore(now)) {
                    log.info("时间到,游戏结束,当前时间:{} 结束时间:{}", LocalDateTime.now(), roofInfoDTO.getEndDateTime());
                    // 结算分数最低者
                    List<ClientInfoDTO> players = roofInfoDTO.getPlayers();
                    Optional<ClientInfoDTO> min = players.stream()
                            .min(Comparator.comparingInt(ClientInfoDTO::getScore));
                    ClientInfoDTO clientInfoDTO = min.get();
                    failure(clientInfoDTO, "");
                    // 给自己发送失败信息
                    // 给房间其他人发送胜利消息
                    SocketMessage socketMessage = new SocketMessage();
                    socketMessage.setEvent(parentPatch(roofInfoDTO.getGameType()) + "/win");
                    socketMessage.setData(0);
                    sendMessage(clientInfoDTO, socketMessage);
                }
            }
        }
    }

    public void usePropSuccess(ClientInfoDTO clientInfoDTO, String message) {
        String roofNo = clientInfoDTO.getRoofNo();
        if (!roofSocketMap.containsKey(roofNo)) {
            log.error("未找到房间:{}", roofNo);
            return;
        }
        RoofInfoDTO roofInfoDTO = roofSocketMap.get(roofNo);
        SocketMessage<UsePropDTO> usePropDTO = JSON.parseObject(message, new TypeReference<SocketMessage<UsePropDTO>>() {
        });
        List<ClientInfoDTO> opponent = getOpponent(roofInfoDTO.getPlayers(), clientInfoDTO);
        ClientInfoDTO propClient = opponent.get(0);
        // 当前道具去除
        for (int i = 0; i < propClient.getProps().size(); i++) {
            Prop prop = propClient.getProps().get(i);
            if (usePropDTO.getData().getProp().getId().equals(prop.getId())) {
                propClient.getProps().remove(i);
                break;
            }
        }
        SocketMessage socketMessage = new SocketMessage(parentPatch(roofInfoDTO.getGameType()) + "/enemyPropSuccess",
                usePropDTO.getData());
        sendMessage(propClient, socketMessage);

        if (!usePropDTO.getData().isSuccess()) {

            return;
        }

        switch (usePropDTO.getData().getProp().getType()) {
            case 50: {
                this.boomSuccess(clientInfoDTO, propClient, usePropDTO.getData());
                break;
            }
            case 51: {
                this.frozenSuccess(clientInfoDTO, propClient, usePropDTO.getData());
                break;
            }
            case 100: {
                this.reversedSuccess(clientInfoDTO, propClient, usePropDTO.getData());
                break;
            }
            case 101: {
                this.curseSuccess(clientInfoDTO, propClient, usePropDTO.getData());
                break;
            }
            case 151: {
                this.formulateSuccess(clientInfoDTO, propClient, usePropDTO.getData());
                break;
            }

        }


    }

    /**
     * 策反
     *
     * @param clientInfoDTO
     * @param propClient
     * @param usePropDTO
     */
    private void formulateSuccess(ClientInfoDTO clientInfoDTO, ClientInfoDTO propClient, UsePropDTO usePropDTO) {
        if (CollUtil.isNotEmpty(usePropDTO.getSelectEnemyPosition())) {
            if (clientInfoDTO.getCurrent() == null) {
                clientInfoDTO.setCurrent(clientInfoDTO.getBefore());
            }
            clientInfoDTO.setBefore(clientInfoDTO.getCurrent());
            List<List<Vec3DTO>> current = clientInfoDTO.getCurrent();
            Vec3DTO vec3DTO = current.get((int) usePropDTO.getSelectEnemyPosition().get(0).getX())
                    .get((int) usePropDTO.getSelectEnemyPosition().get(0).getY());
            vec3DTO.setStatus(usePropDTO.getSelectEnemyPosition().get(0).getStatus());
        }
    }


    private void curseSuccess(ClientInfoDTO clientInfoDTO, ClientInfoDTO propClient, UsePropDTO usePropDTO) {
        // 去除当前棋子
        if (clientInfoDTO.getCurrent() == null) {
            clientInfoDTO.setCurrent(clientInfoDTO.getBefore());
        }
        clientInfoDTO.setBefore(clientInfoDTO.getCurrent());
        List<List<Vec3DTO>> current = clientInfoDTO.getCurrent();

        Vec3DTO vec3DTO = current.get((int) usePropDTO.getSelectEnemyPosition().get(0).getX())
                .get((int) usePropDTO.getSelectEnemyPosition().get(0).getY());
        vec3DTO.setStatus(usePropDTO.getSelectEnemyPosition().get(0).getStatus());
    }

    private void reversedSuccess(ClientInfoDTO clientInfoDTO, ClientInfoDTO propClient, UsePropDTO usePropDTO) {
        // 旋转
        if (clientInfoDTO.getCurrent() == null) {
            clientInfoDTO.setCurrent(clientInfoDTO.getBefore());
        }
        clientInfoDTO.setBefore(clientInfoDTO.getCurrent());
        clientInfoDTO.setCurrent(usePropDTO.getCurrent());
    }


    private void frozenSuccess(ClientInfoDTO clientInfoDTO, ClientInfoDTO propClient, UsePropDTO usePropDTO) {
        if (CollUtil.isNotEmpty(usePropDTO.getSelectEnemyPosition())) {
            if (clientInfoDTO.getCurrent() == null) {
                clientInfoDTO.setCurrent(clientInfoDTO.getBefore());
            }
            clientInfoDTO.setBefore(clientInfoDTO.getCurrent());
            List<List<Vec3DTO>> current = clientInfoDTO.getCurrent();

            Vec3DTO vec3DTO = current.get((int) usePropDTO.getSelectEnemyPosition().get(0).getX())
                    .get((int) usePropDTO.getSelectEnemyPosition().get(0).getY());
            vec3DTO.setStatus(usePropDTO.getSelectEnemyPosition().get(0).getStatus());
        }

    }

    private void boomSuccess(ClientInfoDTO clientInfoDTO, ClientInfoDTO propClient, UsePropDTO usePropDTO) {
        if (CollUtil.isNotEmpty(usePropDTO.getSelectEnemyPosition())) {
            // 去除当前棋子
            if (clientInfoDTO.getCurrent() == null) {
                clientInfoDTO.setCurrent(clientInfoDTO.getBefore());
            }
            clientInfoDTO.setBefore(clientInfoDTO.getCurrent());
            List<List<Vec3DTO>> current = clientInfoDTO.getCurrent();
            Vec3DTO vec3DTO = current.get((int) usePropDTO.getSelectEnemyPosition().get(0).getX())
                    .get((int) usePropDTO.getSelectEnemyPosition().get(0).getY());
            vec3DTO.setZ(0);
            vec3DTO.setStatus(new ArrayList<>());
        }

    }

    public void cancelMatch(ClientInfoDTO clientInfoDTO) {
        clientInfoDTO.setMatch(false);
        match2048Linked.remove(clientInfoDTO);
        matchxiaoxiaoleLinked.remove(clientInfoDTO);
    }

    public void createRoom(ClientInfoDTO clientInfoDTO, String message) {
        SocketMessage<CreateRoofInfoDTO> createRoofInfoDTOSocketMessage = JSON.parseObject(message, new TypeReference<SocketMessage<CreateRoofInfoDTO>>() {
        });
        String roofNo = IdUtil.simpleUUID();
        clearInfo(clientInfoDTO);
        clientInfoDTO.setRoofNo(roofNo);
        clientInfoDTO.setRoofNumber(1);
        clientInfoDTO.setStatus(1);

        RoofInfoDTO roofInfoDTO = new RoofInfoDTO();
        roofInfoDTO.setRoofNo(roofNo);
        roofInfoDTO.setStatus(0);
        roofInfoDTO.setPlayers(CollUtil.newArrayList(clientInfoDTO));
        roofInfoDTO.setGameType(createRoofInfoDTOSocketMessage.getData().getGameType());
        roofSocketMap.put(roofNo, roofInfoDTO);


        SocketMessage socketMessage = new SocketMessage("home/createRoomSuccess",
                roofInfoDTO);
        sendMessage(clientInfoDTO, socketMessage);

    }

    public void joinRoom(ClientInfoDTO clientInfoDTO, String message) {
        SocketMessage<String> joinRoofInfoDTOSocketMessage = JSON.parseObject(message, new TypeReference<SocketMessage<String>>() {
        });
        String roofNo = joinRoofInfoDTOSocketMessage.getData();
        if (!roofSocketMap.containsKey(roofNo)) {
            log.error("未找到房间:{}", roofNo);
            SocketMessage socketMessage = new SocketMessage("GameRoom/joinRoomFail",
                    "房间已解散");
            sendMessage(clientInfoDTO, socketMessage);
            return;
        }
        RoofInfoDTO roofInfoDTO = roofSocketMap.get(roofNo);

        // 如果已经在房间,直接发送成功信息
        if (roofInfoDTO.getPlayers().contains(clientInfoDTO)) {
            SocketMessage socketMessage = new SocketMessage("GameRoom/joinRoomSuccess",
                    roofInfoDTO);
            sendMessage(clientInfoDTO, socketMessage);
            return;
        }


        if (roofInfoDTO.getPlayers().size() >= 2) {
            SocketMessage socketMessage = new SocketMessage("GameRoom/joinRoomFail",
                    "房间已满员");
            sendMessage(clientInfoDTO, socketMessage);
            return;
        }
        clearInfo(clientInfoDTO);
        clientInfoDTO.setRoofNo(roofNo);
        clientInfoDTO.setRoofNumber(roofInfoDTO.getPlayers().size() + 1);
        clientInfoDTO.setStatus(1);
        roofInfoDTO.getPlayers().add(clientInfoDTO);

        SocketMessage socketMessage = new SocketMessage("GameRoom/joinRoomSuccess",
                roofInfoDTO);

        // 房间所有玩家广播房间信息
        for (ClientInfoDTO player : roofInfoDTO.getPlayers()) {
            sendMessage(player, socketMessage);
        }
    }

    public void exitRoom(ClientInfoDTO entity, String message) {

        String roofNo = entity.getRoofNo();
        log.info("退出房间:{} 玩家：{}", roofNo, entity.getCurrentPlayer().getId());
        RoofInfoDTO roofInfoDTO = roofSocketMap.get(roofNo);
        if (roofInfoDTO == null) {
            return;
        }

        // 游戏中退出
        if (roofInfoDTO.getStatus() == 1) {
            this.gameExit(entity, roofInfoDTO);
            return;
        }

        SocketMessage<String> exitRoomMessage = JSON.parseObject(message, new TypeReference<SocketMessage<String>>() {
        });

        // 游戏结束退出
        if (roofInfoDTO.getStatus() == 2) {


            roofInfoDTO.getPlayers().remove(entity);
            clearInfo(entity);
            List<ClientInfoDTO> opponent = this.getOpponent(roofInfoDTO.getPlayers(), entity);
            if (!isEmptyPlayer(opponent)) {
                SocketMessage socketMessage = new SocketMessage(exitRoomMessage.getData() + "/otherExitRoom", roofInfoDTO);
                this.sendMessage(opponent, socketMessage);
            } else {
                removeRoof(roofNo);
            }

            return;
        }

        if (roofInfoDTO.getPlayers().getFirst() == entity) {
            removeRoof(roofNo);
            List<ClientInfoDTO> opponent = this.getOpponent(roofInfoDTO.getPlayers(), entity);
            SocketMessage socketMessage = new SocketMessage(exitRoomMessage.getData() + "/disbandedRoomSuccess",
                    roofInfoDTO);
            this.sendMessage(opponent, socketMessage);
            for (ClientInfoDTO player : roofInfoDTO.getPlayers()) {
                clearInfo(player);
            }
        } else {
            roofInfoDTO.getPlayers().remove(entity);
            // 通知房间其他选手
            List<ClientInfoDTO> opponent = this.getOpponent(roofInfoDTO.getPlayers(), entity);
            SocketMessage socketMessage = new SocketMessage(exitRoomMessage.getData() + "/otherExitRoom", roofInfoDTO);
            this.sendMessage(opponent, socketMessage);
            clearInfo(entity);
        }

    }

    private void gameExit(ClientInfoDTO entity, RoofInfoDTO roofInfoDTO) {
        List<ClientInfoDTO> opponent = this.getOpponent(roofInfoDTO.getPlayers(), entity);
        SocketMessage socketMessage = new SocketMessage("manyGame2048/otherExitRoom", roofInfoDTO);
        this.sendMessage(opponent, socketMessage);
        this.exit(entity);

    }

    public void updatePlayerStatus(ClientInfoDTO entity, String message) {
        SocketMessage<Integer> joinRoofInfoDTOSocketMessage = JSON.parseObject(message, new TypeReference<SocketMessage<Integer>>() {
        });
        entity.setStatus(joinRoofInfoDTOSocketMessage.getData());

        RoofInfoDTO roofInfoDTO = roofSocketMap.get(entity.getRoofNo());

        // 通知房间其他选手
        List<ClientInfoDTO> opponent = this.getOpponent(roofInfoDTO.getPlayers(), entity);
        SocketMessage socketMessage = new SocketMessage(joinRoofInfoDTOSocketMessage.getMessage() + "/otherUpdatePlayerStatus", entity);
        this.sendMessage(opponent, socketMessage);

        // 游戏结束,并且两人都准备了
        if (roofInfoDTO.getPlayers().size() > 1 && roofInfoDTO.getStatus() == 2) {
            if (roofInfoDTO.getPlayers().stream().allMatch(item -> item.getStatus() == 1)) {
                roomStartGame(entity, message);
            }
        }

    }

    public void roomStartGame(ClientInfoDTO entity, String message) {
        SocketMessage joinRoofInfoDTOSocketMessage = JSON.parseObject(message, SocketMessage.class);
        String roofNo = entity.getRoofNo();
        RoofInfoDTO roofInfoDTO = roofSocketMap.get(roofNo);
        roofInfoDTO.setStatus(1);
        roofInfoDTO.setDateTime(LocalDateTime.now());
        roofInfoDTO.setStartDateTime(roofInfoDTO.getDateTime().plusSeconds(5));
        roofInfoDTO.setEndDateTime(roofInfoDTO.getStartDateTime().plusMinutes(2));
        List<ClientInfoDTO> players = roofInfoDTO.getPlayers();

        for (ClientInfoDTO player : players) {
            player.setCurrent(null);
            player.setProps(new ArrayList<>());
            player.setScore(0);
            if (roofInfoDTO.getGameType().equals("2048")) {
                player.setLv(GameUserService.lv2048);
                player.setBefore(GameUserService.init2048());
            } else if (roofInfoDTO.getGameType().equals("xiaoxiaole")) {
                player.setLv(GameUserService.lvXiaoXiaoLe);
                player.setBefore(GameUserService.initXiaoxiaole());
            }
        }

        SocketMessage socketMessage = new SocketMessage();
        socketMessage.setEvent(joinRoofInfoDTOSocketMessage.getMessage() + "/StartGame");
        socketMessage.setData(roofInfoDTO);
        sendMessage(roofInfoDTO.getPlayers(), socketMessage);
    }


    /**
     * 匹配逻辑
     */
    public void matchLogic() {
        // 匹配队列有人
        if (!match2048Linked.isEmpty()) {
            int plusSeconds = 0;
            for (ClientInfoDTO clientInfoDTO : match2048Linked) {
                // 2秒后还是没匹配到人,ai匹配
                if (clientInfoDTO.isMatch()
                        && clientInfoDTO.getMatchTime().plusSeconds(plusSeconds).isBefore(LocalDateTime.now())) {
                    WxUser ai = wxUserService.getAi(clientInfoDTO.getCurrentPlayer().getId());
                    ClientInfoDTO aiClientInfoDTO = new ClientInfoDTO();
                    aiClientInfoDTO.setAI(true);
                    aiClientInfoDTO.setStatus(1);
                    aiClientInfoDTO.setAiNo(IdUtil.simpleUUID());
                    aiClientInfoDTO.setScore(0);
                    CurrentPlayer currentPlayer = new CurrentPlayer();
                    currentPlayer.setId(-1);
                    currentPlayer.setWxId("ai");
                    currentPlayer.setWxUsername(ai.getWxUsername());
                    currentPlayer.setWxHead(ai.getWxHead());
                    currentPlayer.setGame2048Level(clientInfoDTO.getCurrentPlayer().getGame2048Level());
                    aiClientInfoDTO.setCurrentPlayer(currentPlayer);
                    this.match2048(aiClientInfoDTO);
                }
            }
        }

        // ai移动
        aiLogic.move();
    }

    private void removeRoof(String roofNo) {
        roofSocketMap.remove(roofNo);
        this.aiLogic.removeAI(roofNo);
    }

    private boolean isEmptyPlayer(List<ClientInfoDTO> clientInfoDTOS) {
        if (CollUtil.isEmpty(clientInfoDTOS)) {
            return true;
        }
        // 全是ai
        return clientInfoDTOS.stream().allMatch(ClientInfoDTO::isAI);

    }
}
